package zlog

import (
	"bytes"
	"encoding/json"
	"fmt"
	"net/http"
	"os"
	"reflect"
	"strings"
	"time"

	"gitee.com/xchengli/blog-lib/components"
	"gitee.com/xchengli/blog-lib/defines"

	"github.com/gin-gonic/gin"
	"go.uber.org/zap"
)

type zapFiledKv struct {
	key   string
	value interface{}
}

func extFields(ctx *gin.Context) []zapFiledKv {
	return []zapFiledKv{
		{Field4Time, time.Now().Format(time.DateTime)},
		{Field4Method, getMethodByCtx(ctx)},
		{Field4URL, getURLByCtx(ctx)},
		{Field4RequestForm, getRequestFormByCtx(ctx)},
		{Field4RequestBody, getRequestBodyByCtx(ctx)},
		{Field4IP, components.GetLocalIp()},
		{Field4LogId, getLogIdByCtx(ctx)},
	}
}

func commonFields(ctx *gin.Context) zap.Option {
	arr := extFields(ctx)
	var opt []zap.Field
	for _, v := range arr {
		opt = append(opt, zap.String(v.key, fmt.Sprintf("%v", v.value)))
	}
	return zap.Fields(opt...)
}

//--------------------- debug日志信息 ---------------------

func debug(ctx *gin.Context, args ...interface{}) {
	if infoSugaredLogger == nil {
		InitLog(logPath, logFileName, level, stdout)
	}
	infoSugaredLogger.WithOptions(commonFields(ctx)).Debug(args)
}
func debugW(ctx *gin.Context, msg string, keysAndValues ...interface{}) {
	if infoSugaredLogger == nil {
		InitLog(logPath, logFileName, level, stdout)
	}
	infoSugaredLogger.WithOptions(commonFields(ctx)).Debugw(msg, keysAndValues...)
}
func debugF(ctx *gin.Context, template string, args ...interface{}) {
	if infoSugaredLogger == nil {
		InitLog(logPath, logFileName, level, stdout)
	}
	infoSugaredLogger.WithOptions(commonFields(ctx)).Debugf(template, args...)
}

//--------------------- 一般日志信息 ---------------------

func DebugW(ctx *gin.Context, msg string, keysAndValues ...interface{}) {
	if infoSugaredLogger == nil {
		InitLog(logPath, logFileName, level, stdout)
	}
	infoSugaredLogger.WithOptions(commonFields(ctx)).Debugw(msg, keysAndValues...)
}
func WarnW(ctx *gin.Context, msg string, keysAndValues ...interface{}) {
	if infoSugaredLogger == nil {
		InitLog(logPath, logFileName, level, stdout)
	}
	infoSugaredLogger.WithOptions(commonFields(ctx)).Warnw(msg, keysAndValues...)
}

func FatalW(ctx *gin.Context, msg string, keysAndValues ...interface{}) {
	if infoSugaredLogger == nil {
		InitLog(logPath, logFileName, level, stdout)
	}
	infoSugaredLogger.WithOptions(commonFields(ctx)).Fatalw(msg, keysAndValues...)
}

//--------------------- 一般日志信息 ---------------------

func Info(ctx *gin.Context, args ...interface{}) {
	if infoSugaredLogger == nil {
		InitLog(logPath, logFileName, level, stdout)
	}
	infoSugaredLogger.WithOptions(commonFields(ctx)).Info(args)
}
func InfoW(ctx *gin.Context, msg string, keysAndValues ...interface{}) {
	if infoSugaredLogger == nil {
		InitLog(logPath, logFileName, level, stdout)
	}
	infoSugaredLogger.WithOptions(commonFields(ctx)).Infow(msg, keysAndValues...)
}
func InfoF(ctx *gin.Context, template string, args ...interface{}) {
	if infoSugaredLogger == nil {
		InitLog(logPath, logFileName, level, stdout)
	}
	infoSugaredLogger.WithOptions(commonFields(ctx)).Infof(template, args...)
}

//--------------------- 错误日志 ---------------------

func Error(ctx *gin.Context, args ...interface{}) {
	if errorSugaredLogger == nil {
		InitLog(logPath, logFileName, level, stdout)
	}
	errorSugaredLogger.WithOptions(commonFields(ctx)).Error(args)
}
func Errorf(ctx *gin.Context, template string, args ...interface{}) {
	if errorSugaredLogger == nil {
		InitLog(logPath, logFileName, level, stdout)
	}
	errorSugaredLogger.WithOptions(commonFields(ctx)).Errorf(template, args...)
}
func ErrorW(ctx *gin.Context, msg string, keysAndValues ...interface{}) {
	if errorSugaredLogger == nil {
		InitLog(logPath, logFileName, level, stdout)
	}
	errorSugaredLogger.WithOptions(commonFields(ctx)).Errorw(msg, keysAndValues...)
}

//--------------------- 发消息提醒 ---------------------

func NoticeErr(ctx *gin.Context, arg string) {
	if errorSugaredLogger == nil {
		InitLog(logPath, logFileName, level, stdout)
	}
	extField := extFields(ctx)
	var fieldsStr string
	for _, v := range extField {
		// value转json
		bt, _ := json.Marshal(v.value)
		fieldsStr += fmt.Sprintf("%s: %s\n", v.key, string(bt))
	}
	fullMessage := fmt.Sprintf("%v\n%s", arg, fieldsStr)
	sendMsg(ctx, fullMessage)
	errorSugaredLogger.WithOptions(commonFields(ctx)).Error(arg)
}

// NoticeErrF
func NoticeErrF(ctx *gin.Context, template string, args ...interface{}) {
	if errorSugaredLogger == nil {
		InitLog(logPath, logFileName, level, stdout)
	}
	extField := extFields(ctx)
	var fieldsStr string
	for _, v := range extField {
		// value转json
		bt, _ := json.Marshal(v.value)
		fieldsStr += fmt.Sprintf("%s: %s\n", v.key, string(bt))
	}
	fullMessage := fmt.Sprintf("%v\n%s", fmt.Sprintf(template, args...), fieldsStr)
	sendMsg(ctx, fullMessage)
	errorSugaredLogger.WithOptions(commonFields(ctx)).Errorf(template, args)
}

// NoticeErrW
func NoticeErrW(ctx *gin.Context, msg string, keysAndValues ...interface{}) {
	if errorSugaredLogger == nil {
		InitLog(logPath, logFileName, level, stdout)
	}
	extMsg := ""
	for i := 0; i < len(keysAndValues); i += 2 {
		if i+1 < len(keysAndValues) {
			key := keysAndValues[i]
			val := keysAndValues[i+1]
			// 通过反射获取val的类型，如果是字符串或者数字类型，则直接输出，否则转json
			if components.InArray(reflect.TypeOf(val).Kind(), []reflect.Kind{
				reflect.String,
				reflect.Uint,
				reflect.Uint8,
				reflect.Uint32,
				reflect.Uint64,
				reflect.Int,
				reflect.Int8,
				reflect.Int32,
				reflect.Int64,
				reflect.Float32,
				reflect.Float64,
			}) {
				extMsg += fmt.Sprintf("%s: %v\n", key, val)
			} else {
				bt, _ := json.Marshal(keysAndValues[i+1])
				extMsg += fmt.Sprintf("%s: %v\n", key, string(bt))
			}
		}
	}
	extField := extFields(ctx)
	var fieldsStr string
	for _, v := range extField {
		// value转json
		bt, _ := json.Marshal(v.value)
		fieldsStr += fmt.Sprintf("%s: %s\n", v.key, string(bt))
	}
	fullMessage := fmt.Sprintf("%v\n%s\n%s", msg, extMsg, fieldsStr)
	sendMsg(ctx, fullMessage)
	errorSugaredLogger.WithOptions(commonFields(ctx)).Errorw(msg, keysAndValues...)
}

// 默认发送到`用户平台和用户链路`
var webhookKey = ""

func SetWebhookKey(key string) {
	webhookKey = key
}
func GetWebhookKey() string {
	return webhookKey
}

func sendMsg(ctx *gin.Context, msg string) {
	// 删除 app.yaml 中 log.webhookKey的值即可关闭发送消息
	if webhookKey == "" {
		return
	}

	switch os.Getenv("APP_ENV") {
	case defines.ENV4Pro:
		msg = fmt.Sprintf("env:production\n%s", msg)
	case defines.ENV4Test:
		msg = fmt.Sprintf("env:test\n%s", msg)
	case defines.ENV4Dev:
		msg = fmt.Sprintf("env:develop\n%s", msg)
	default:
		msg = fmt.Sprintf("env:null\n%s", msg)
	}
	msg = strings.Trim(msg, "\n")

	data := map[string]interface{}{
		"msgtype": "text",
		"text": map[string]string{
			"content": msg,
		},
	}
	payload, _ := json.Marshal(data)
	url := "https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=" + webhookKey
	req, err := http.NewRequest("POST", url, bytes.NewBuffer(payload))
	if err != nil {
		return
	}
	req.Header.Set("Content-Type", "application/json")
	client := &http.Client{}
	resp, err := client.Do(req)
	if err != nil {
		ErrorW(ctx, err.Error(), "msg", msg)
		return
	}
	_ = resp.Body.Close()
	return
}
